<template>
	<div style="display:flex; height:100%;">
		<!-- 左：画布和工具栏 -->
		<div style="flex:1; display:flex; flex-direction:column; height:100%;">
			<div style="padding:8px; display:flex; gap:8px; flex-wrap:wrap;">
				<a-button @click="zoomIn">放大</a-button>
				<a-button @click="zoomOut">缩小</a-button>
				<a-button @click="fitView">自适应</a-button>
				<a-button @click="selectAll">全选</a-button>
				<a-button @click="copySelected">复制</a-button>
				<a-button @click="paste">粘贴</a-button>
				<a-button danger @click="deleteSelected">删除</a-button>
				<a-button danger @click="deleteAll">删除全部</a-button>
				<a-button @click="exportData">数据输出</a-button>
			</div>
			<!-- 画布容器 -->
			<div ref="containerRef" class="x6-container" style="flex:1;"></div>
		</div>

		<!-- 右：属性面板 -->
		<div style="width:25%; border-left:1px solid #eee; padding:12px; overflow:auto;">
			<h3>属性面板</h3>
			<div v-if="activeNodeId">
				<a-form layout="vertical" ref="nodeFormRef" :model="formModel">
					<a-form-item label="节点名称" name="formLabel" :rules="formRules.formLabel">
						<a-input v-model:value="formModel.formLabel" />
					</a-form-item>
					<a-form-item label="操作类型">
						<a-input v-model:value="formModel.formType" :disabled="true" />
					</a-form-item>

					<!-- 头节点只显示名称，操作节点显示参数和配置 -->
					<template v-if="formModel.formType === 'action'">
						<a-form-item label="参数" name="formParams" :rules="formRules.formParams">
							<a-input v-model:value="formModel.formParams" placeholder="请输入参数" />
						</a-form-item>
						<a-form-item label="配置" name="formConfig" :rules="formRules.formParams">
							<a-textarea v-model:value="formModel.formConfig" :rows="4" placeholder="请输入配置" />
						</a-form-item>
						<a-form-item label="指定输出" name="formOutput" :rules="formRules.formParams">
							<a-input v-model:value="formModel.formOutput" placeholder="请输入指定输出" />
						</a-form-item>
					</template>

					<a-space>
						<a-button type="primary" @click="applyNodeEdit">保存</a-button>
						<a-button @click="resetPanel">重置</a-button>
					</a-space>
				</a-form>
			</div>
			<div v-else>未选中节点</div>

			<!-- 添加节点统计信息 -->
			<div style="margin-top: 20px; padding-top: 20px; border-top: 1px solid #eee;">
				<h4>画布统计</h4>
				<p>节点数量: {{ nodeCount }}</p>
				<p>边数量: {{ edgeCount }}</p>
			</div>
		</div>
	</div>
</template>

<script setup lang="ts">
import { onMounted, onBeforeUnmount, ref } from 'vue'
import { Graph } from '@antv/x6'
import { Selection } from '@antv/x6-plugin-selection'
import { Scroller } from '@antv/x6-plugin-scroller'
import { Keyboard } from '@antv/x6-plugin-keyboard'
import { History } from '@antv/x6-plugin-history'
import { Clipboard } from '@antv/x6-plugin-clipboard'

// 画布容器
const containerRef = ref<HTMLDivElement | null>(null)
const nodeFormRef = ref()
let graph: Graph | null = null

// 属性面板状态
const formModel = ref({
	formLabel: '',
	formParams: '',
	formType: 'head' as 'head' | 'action',
	formConfig: '',
	formOutput: ''
})

// 表单校验规则
const formRules = {
	formLabel: [{ required: true, message: '请输入节点名称' }],
	formParams: [{ required: true, message: '请输入参数' }]
}

const activeNodeId = ref<string | null>(null)
// 节点统计信息
const nodeCount = ref(0)
const edgeCount = ref(0)

// 更新统计信息
function updateStatistics() {
	if (!graph) return
	nodeCount.value = graph.getNodes().length
	edgeCount.value = graph.getEdges().length
}

// 拖拽处理函数
function handleDragOver(event: DragEvent) {
	event.preventDefault()
	if (event.dataTransfer) {
		event.dataTransfer.dropEffect = 'copy'
	}
}

function handleDrop(event: DragEvent) {
	event.preventDefault()
	if (!graph || !event.dataTransfer) return
	const operationData = event.dataTransfer.getData('application/operation')
	if (!operationData) return

	try {
		const op = JSON.parse(operationData)
		const container = containerRef.value
		if (!container) return
		const rect = container.getBoundingClientRect()
		const x = event.clientX - rect.left
		const y = event.clientY - rect.top

		const scroller = graph.getPlugin?.('scroller')
		let point
		if (scroller) {
			const clientX = rect.left + x
			const clientY = rect.top + y
			point = graph.clientToLocal?.(clientX, clientY)
		} else {
			point = graph.clientToLocal?.(event.clientX, event.clientY)
		}
		if (!point) return

		const nodeWidth = 100
		const nodeHeight = 40
		point.x -= nodeWidth / 2
		point.y -= nodeHeight / 2

		createActionNodeAt?.({
			code: op.code,
			label: op.label,
			x: point.x,
			y: point.y,
			type: op.type,
			params: op.params,
			config: op.config,
			output: op.output
		})
	} catch (e) {
		console.error('Failed to parse operation data:', e)
	}
}

// —— 初始化 X6 —— //
onMounted(() => {
	graph = new Graph({
		container: containerRef.value!,
		grid: true,
		background: { color: '#f7f8fa' },
		connecting: {
			allowBlank: false,
			allowLoop: false,
			allowMulti: 'withPort',
			highlight: true,
			snap: true,
			router: 'orth',
			connector: 'rounded',
			validateConnection({ sourceView, targetView, targetMagnet }) {
				return !!(sourceView && targetView && targetMagnet)
			},
			createEdge() {
				return graph!.createEdge({
					attrs: { line: { stroke: '#A2B1C3', strokeWidth: 2, targetMarker: 'classic' } }
				})
			}
		},
		highlighting: {
			default: {
				name: 'stroke',
				args: {
					padding: 4,
					attrs: {
						stroke: '#1890ff',
						strokeWidth: 2,
					}
				}
			}
		}
	})

	// 添加插件
	const selection = new Selection({
		enabled: true,
		multiple: true,
		rubberband: true,
		modifiers: 'shift',
		showNodeSelectionBox: true,
		showEdgeSelectionBox: true,
		movable: true,
		strict: false
	})
	graph.use(selection)

	const scroller = new Scroller({ enabled: true, pannable: true })
	graph.use(scroller)

	const keyboard = new Keyboard({ enabled: true })
	graph.use(keyboard)

	const history = new History({ enabled: true })
	graph.use(history)

	const clipboard = new Clipboard({ enabled: true })
	graph.use(clipboard)

	keyboard.bindKey(['backspace', 'delete'], () => deleteSelected())
	keyboard.bindKey(['ctrl+c', 'command+c'], () => copySelected())
	keyboard.bindKey(['ctrl+v', 'command+v'], () => paste())
	keyboard.bindKey(['ctrl+a', 'command+a'], () => selectAll())
	keyboard.bindKey(['shift+backspace', 'shift+delete'], () => deleteAllSelected())

	// 选中变化 -> 更新属性面板
	graph.on('selection:changed', () => {
		const cells = graph!.getSelectedCells()
		const node = cells.find((c) => c.isNode())

		nodeFormRef.value?.resetFields()

		if (node && node.isNode()) {
			activeNodeId.value = node.id
			const d = node.getData() || {}
			formModel.value.formLabel = d.label || node.attr('label/text') || ''
			// 修复：只设置一次formType，使用节点的type属性
			formModel.value.formType = (d.type as 'head' | 'action') || 'head'

			if (formModel.value.formType === 'action') {
				formModel.value.formParams = d.params || ''
				formModel.value.formConfig = d.config || ''
				formModel.value.formOutput = d.output || ''
			} else {
				formModel.value.formParams = ''
				formModel.value.formConfig = ''
				formModel.value.formOutput = ''
			}
		} else {
			resetPanel()
		}
	})

	graph.on('cell:added', updateStatistics)
	graph.on('cell:removed', updateStatistics)
	updateStatistics()

	if (containerRef.value) {
		containerRef.value.addEventListener('dragover', handleDragOver, false)
		containerRef.value.addEventListener('drop', handleDrop, false)
	}
})

onBeforeUnmount(() => {
	if (containerRef.value) {
		containerRef.value.removeEventListener('dragover', handleDragOver)
		containerRef.value.removeEventListener('drop', handleDrop)
	}
	graph?.dispose()
	graph = null
})

// —— 工具方法 —— //
function zoomIn() { graph?.zoom(0.1) }
function zoomOut() { graph?.zoom(-0.1) }
function fitView() {
	if (!graph) return
	const scrollerPlugin = graph.getPlugin('scroller') as Scroller
	if (scrollerPlugin) scrollerPlugin.centerContent()
	else graph.centerContent()
}
// —— CRUD —— //
function deleteSelected() {
	if (!graph) return
	const cells = graph.getSelectedCells()
	if (cells.length) graph.removeCells(cells)
}

// 复制选中节点
function copySelected() {
	if (!graph) return
	const cells = graph.getSelectedCells()
	if (cells.length) {
		graph.copy(cells, { deep: true })
		console.log(`已复制 ${cells.length} 个元素`)
	} else {
		console.log('没有选中任何节点或边')
	}
}

// 粘贴
function paste() {
	if (!graph) return
	const pasted = graph.paste({ offset: 20 })
	graph.cleanSelection()
	graph.select(pasted)
	console.log(`已粘贴 ${pasted.length} 个元素`)
}

// 删除所有节点
function deleteAll() {
	if (!graph) return
	const cells = graph.getCells()
	if (cells.length && confirm('确定要删除所有节点吗？')) {
		graph.removeCells(cells)
	}
}

// 添加节点
function addNode(payload: {
	id?: string
	x: number
	y: number
	label: string
	type: 'head' | 'action'
	code?: string
	params?: string
	config?: string
	output?: string
	actionType?: string
}) {
	if (!graph) return
	const initialWidth = 160
	const initialHeight = 40
	const fontSize = 14
	const fontWeight = 600
	const fontFamily = 'sans-serif'

	const node = graph.addNode({
		id: payload.id,
		x: payload.x,
		y: payload.y,
		width: initialWidth,
		height: initialHeight,
		shape: 'rect',
		data: {
			label: payload.label,
			type: payload.type,
			code: payload.code,
			params: payload.params,
			config: payload.config,
			output: payload.output,
			actionType: payload.actionType
		},
		attrs: {
			body: {
				fill: payload.type === 'head' ? '#fff7e6' : '#e6f7ff',
				stroke: payload.type === 'head' ? '#ffd591' : '#91d5ff',
				strokeWidth: 2,
				rx: 8,
				ry: 8
			},
			label: {
				text: payload.label,
				fontSize,
				fontWeight,
				fill: '#333',
				refX: '50%',
				refY: '50%',
				textAnchor: 'middle',
				textVerticalAnchor: 'middle',
				style: { fontFamily }
			}
		},
		ports: {
			groups: {
				top: { position: 'top', attrs: { circle: { r: 4, magnet: true, stroke: '#999', fill: '#fff' } } },
				right: { position: 'right', attrs: { circle: { r: 4, magnet: true, stroke: '#999', fill: '#fff' } } },
				bottom: { position: 'bottom', attrs: { circle: { r: 4, magnet: true, stroke: '#999', fill: '#fff' } } },
				left: { position: 'left', attrs: { circle: { r: 4, magnet: true, stroke: '#999', fill: '#fff' } } }
			},
			items: [
				{ group: 'top', id: `${payload.id}-t` },
				{ group: 'right', id: `${payload.id}-r` },
				{ group: 'bottom', id: `${payload.id}-b` },
				{ group: 'left', id: `${payload.id}-l` }
			]
		}
	})

	setTimeout(() => {
		if (!node || !graph?.getCellById(node.id)) return
		const text = payload.label || ''
		const minWidth = 100
		const ctx = document.createElement('canvas').getContext('2d')!
		ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`
		const textWidth = ctx.measureText(text).width

		const paddingX = fontSize * 1.5
		const paddingY = fontSize * 0.5
		const finalWidth = Math.max(minWidth, textWidth + paddingX)
		const finalHeight = fontSize + paddingY * 2

		node.resize(finalWidth, finalHeight)

		const halfWidth = finalWidth / 2
		const halfHeight = finalHeight / 2
		node.getPorts().forEach(port => {
			const id = port.id!
			let pos
			switch (port.group) {
				case 'top': pos = { x: halfWidth, y: 0 }; break
				case 'bottom': pos = { x: halfWidth, y: finalHeight }; break
				case 'left': pos = { x: 0, y: halfHeight }; break
				case 'right': pos = { x: finalWidth, y: halfHeight }; break
			}
			if (pos) node.setPortProp(id, 'args', pos)
		})
	}, 0)
}

// 保存节点数据
function saveNodeData() {
	if (!graph || !activeNodeId.value) return
	const node = graph.getCellById(activeNodeId.value)
	if (!node || !node.isNode()) return

	if (formModel.value.formType === 'action') {
		node.setData({ ...(node.getData() || {}), label: formModel.value.formLabel, params: formModel.value.formParams, config: formModel.value.formConfig, output: formModel.value.formOutput, type: formModel.value.formType, actionType: formModel.value.formType })
	} else {
		node.setData({ ...(node.getData() || {}), label: formModel.value.formLabel, type: formModel.value.formType })
	}
	node.attr('label/text', formModel.value.formLabel)
	node.attr('body/fill', formModel.value.formType === 'head' ? '#fff7e6' : '#e6f7ff')
	node.attr('body/stroke', formModel.value.formType === 'head' ? '#ffd591' : '#91d5ff')
}

// 应用属性面板修改
function applyNodeEdit() {
	if (!graph || !activeNodeId.value) return
		nodeFormRef.value?.validate().then(() => {
			saveNodeData()
		}).catch((err: any) => {
			console.log('校验失败:', err)
			// AntD Vue 会自动高亮错误
		})
}

// 重置属性面板
function resetPanel() {
	activeNodeId.value = null
	formModel.value.formLabel = ''
	formModel.value.formParams = ''
	formModel.value.formConfig = ''
	formModel.value.formOutput = ''
	formModel.value.formType = 'head'
	formModel.value.formType = 'head'
}

// 创建节点（批量从列表）
function createNodesFromList(list: Array<{ id: string; name: string }>) {
	if (!graph) return
	const baseY = graph.getNodes().length * 40
	list.forEach((item, idx) =>
		addNode({
			id: item.id,
			x: 80,
			y: baseY + idx * 40,
			label: item.name,
			type: 'head'
		})
	)
}

// 拖拽创建操作节点
function createActionNodeAt(op: { code: string; label: string; x: number; y: number; type?: string; params?: string; config?: string; output?: string }) {
	if (!graph) return
	addNode({
		id: `action-${op.code}-${Date.now()}`,
		x: op.x,
		y: op.y,
		label: `操作：${op.label}`,
		type: 'action',
		code: op.code,
		actionType: op.type || '',
		params: op.params || '',
		config: op.config || '',
		output: op.output || ''
	})
}

// 批量选择
function selectAll() {
	if (!graph) return
	const allCells = graph.getCells()
	if (allCells.length) graph.select(allCells)
}

// 批量删除选中
function deleteAllSelected() {
	if (!graph) return
	const cells = graph.getSelectedCells()
	if (cells.length) {
		if (confirm(`确定要删除选中的 ${cells.length} 个元素吗？`)) {
			graph.removeCells(cells)
		}
	} else {
		console.log('没有选中任何元素')
	}
}

// 导出数据
function exportData() {
	if (!graph) return
	const nodes = graph.getNodes()
	const edges = graph.getEdges()
	const exportData = {
		nodes: nodes.map(node => ({
			id: node.id,
			x: node.position().x,
			y: node.position().y,
			data: node.getData(),
			size: node.size()
		})),
		edges: edges.map(edge => ({
			id: edge.id,
			source: edge.getSource(),
			target: edge.getTarget(),
			data: edge.getData()
		}))
	}
	console.log('导出的数据:', exportData)
	alert('数据已导出，可以在控制台查看详细信息')
}

defineExpose({
	createNodesFromList,
	createActionNodeAt
})
</script>

<style scoped>
.x6-container {
	width: 100%;
	height: 100%;
}
</style>
